#include "userprog/process.h"
#include <debug.h>
#include <inttypes.h>
#include <round.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "userprog/gdt.h"
#include "userprog/pagedir.h"
#include "userprog/tss.h"
#include "filesys/directory.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/flags.h"
#include "threads/init.h"
#include "threads/interrupt.h"
#include "threads/palloc.h"
#include "threads/thread.h"
#include "threads/vaddr.h"

static thread_func start_process NO_RETURN;
static bool load(const char *cmdline, void (**eip)(void), void **esp);

tid_t process_execute(const char *file_name) {
    char *fn_copy;
    tid_t tid;

    /* Make a copy of FILE_NAME. */
    fn_copy = palloc_get_page(0);
    if (fn_copy == NULL)
        return TID_ERROR;
    strlcpy(fn_copy, file_name, PGSIZE);

    /* Extract program name from command line. */
    char *save_ptr;
    char program_name[16];
    strlcpy(program_name, strtok_r(fn_copy, " ", &save_ptr), sizeof program_name);

    /* Create a new thread to execute the program. */
    tid = thread_create(program_name, PRI_DEFAULT, start_process, fn_copy);
    if (tid == TID_ERROR)
        palloc_free_page(fn_copy);

    return tid;
}

static void start_process(void *file_name_) {
    char *file_name = file_name_;
    struct intr_frame if_;
    bool success;

    memset(&if_, 0, sizeof if_);
    if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
    if_.cs = SEL_UCSEG;
    if_.eflags = FLAG_IF | FLAG_MBS;
    success = load(file_name, &if_.eip, &if_.esp);

    palloc_free_page(file_name);
    if (!success)
        thread_exit();

    asm volatile("movl %0, %%esp; jmp intr_exit" : : "g"(&if_) : "memory");
    NOT_REACHED();
}

int process_wait(tid_t child_tid UNUSED) {
    return -1;
}

void process_exit(void) {
    struct thread *cur = thread_current();

    /* Print exit message for user processes. */
    if (cur->pagedir) { // Only print if it's a user process
        printf("%s: exit(%d)\n", cur->name, cur->exit_status);
    }

    uint32_t *pd = cur->pagedir;
    if (pd != NULL) {
        cur->pagedir = NULL;
        pagedir_activate(NULL);
        pagedir_destroy(pd);
    }
}

void process_activate(void) {
    struct thread *t = thread_current();

    pagedir_activate(t->pagedir);
    tss_update();
}
